﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Tum.CollabXT.DocxGen.Resources;

namespace Tum.CollabXT.DocxGen
{
    /// <summary>
    /// Class with necessary functionalities for setting paragraph texts.
    /// </summary>
    class ParagraphUtils
    {

        #region Attributes
        public const string NamespaceURL = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
        public  const string NamespacePrefix = "w";

        private XmlDocument DocumentXml;
        private XmlNamespaceManager Nsmng;
        #endregion

        #region Constructor
        public ParagraphUtils(XmlDocument documentXML)
        {
            this.DocumentXml = documentXML;
            this.Nsmng = new XmlNamespaceManager(this.DocumentXml.NameTable);
            Nsmng.AddNamespace(NamespacePrefix, NamespaceURL);
        }
        #endregion

        /// <summary>
        /// Sets the text of a T element in a spesific P Element by customTID.
        /// </summary>
        /// <param name="documentNode">document P node.</param>
        /// <param name="ID">The TID.</param>
        /// <param name="text">The text.</param>
        public void SetTextByCustomTID(XmlNode documentNode, string TID, string text)
        {
            documentNode.SelectSingleNode(string.Format(Queries.GetTextByTid, TID), this.Nsmng).InnerText = text;
        }

        /// <summary>
        /// Sets the text by custom TID in an entire document.xml file.
        /// </summary>
        /// <param name="ID">The ID.</param>
        /// <param name="text">The text.</param>
        public void SetTextByCustomTID(string ID, string text)
        {
            this.DocumentXml.SelectSingleNode(string.Format(Queries.GetTextByTid, ID), this.Nsmng).InnerText = text;
        }

        /// <summary>
        /// Selects the XmlNode the by TID in an entire document.xml file.
        /// </summary>
        /// <param name="TID">TID.</param>
        /// <returns>XmlNode T</returns>
        public XmlNode SelectByTID(string TID)
        {
            return this.DocumentXml.SelectSingleNode(string.Format(Queries.GetTextByTid, TID), this.Nsmng);
        }

        /// <summary>
        /// Selects the XmlNode the by TID in an entire document.xml file.
        /// It is possible to specify which one of the T-Nodes has to be selected.
        /// If there are more than 1 T-Nodes available than this method choses the one in row number "count"
        /// </summary>
        /// <param name="TID">The TID.</param>
        /// <param name="count">count specifying the row</param>
        /// <returns>XmlNode T</returns>
        public XmlNode SelectByTID(string TID, int count)
        {
            TID = string.Format("{0}{1}", TID, count.ToString());
            return this.DocumentXml.SelectSingleNode(string.Format(Queries.GetTextByTid, TID), this.Nsmng);
        }

        /// <summary>
        /// Selects the by TID.
        /// </summary>
        /// <param name="TID">The TID.</param>
        /// <returns>XmlNodeList containing the XmlNode elements with the given TID</returns>
        public XmlNodeList SelectByTIDs(string TID)
        {
            return this.DocumentXml.SelectNodes(string.Format(Queries.GetTextByTid, TID), this.Nsmng);
        }

        /// <summary>
        /// Selects the XmlNode the by TID in an entire document.xml file.
        /// It is possible to specify which ones of the T-Nodes has to be selected.
        /// If there are more than 1 T-Nodes available than this method choses the ones in row number "count"
        /// </summary>
        /// <param name="TID">The TID.</param>
        /// <param name="count">count specifying the row</param>
        /// <returns>XmlNodeList of Ts</returns>
        public XmlNodeList SelectByTIDs(string TID, int count)
        {
            TID = string.Format("{0}{1}", TID, count.ToString());
            return this.DocumentXml.SelectNodes(string.Format(Queries.GetTextByTid, TID), this.Nsmng);
        }

        /// <summary>
        /// Given a TID, a list of text values this method gets the T-Nodes via TID, 
        /// copies them with their whole paragraphs sets the Text from the value list.
        /// It is possible to give indexes to the newly produced T-Nodes for addresing them afterwards. This index can be 
        /// static or a up-counting integer. If overwrite then the copied T-Node is overwritten by the first T-Nodes textvalue
        /// </summary>
        /// <param name="TID">TID</param>
        /// <param name="valuesList">list of the text values</param>
        /// <param name="overWrite">if set to <c>true</c> T-Node containing the placeholder gets overwritten</param>
        /// <param name="bIndex">if set to <c>true</c> an index is set to TIDs of the new T-Nodes for addresing purposes</param>
        /// <param name="staticIndex">integer of a static Value</param>
        public void CloneAndEvaluatePs(string TID, string[] values, bool overWrite, bool bIndex, int staticIndex)
        {
            XmlNode refT = this.SelectByTID(TID);
            XmlNode refP = PofT(refT);
            if (values != null)
            {
                for (int i = values.Length - 1; i >= 0; i--)
                {
                    if (i == 0 && overWrite)
                    {
                        SetTextByCustomTID(refT, TID, values[0]);
                    }
                    else
                    {
                        XmlNode clone = refP.Clone();
                        SetTextByCustomTID(clone, TID, values[i]);
                        refP.ParentNode.InsertAfter(clone, refP);
                    }
                }
                EvaluatePs(TID, bIndex, staticIndex);
            }
            else
            {
                refP.ParentNode.RemoveChild(refP);
            }
        }

        /// <summary>
        /// See other overload. Except that this version takes only one value and inserts it a given number of times.
        /// </summary>
        public void CloneAndEvaluatePs(string TID, string values, int count, bool overWrite, bool bIndex, int staticIndex)
        {
            XmlNode refT = this.SelectByTID(TID);
            XmlNode refP = PofT(refT);
            if (values != null)
            {
                for (int i = count - 1; i >= 0; i--)
                {
                    if (i == 0 && overWrite)
                    {
                        SetTextByCustomTID(refT, TID, values);
                    }
                    else
                    {
                        XmlNode clone = refP.Clone();
                        SetTextByCustomTID(clone, TID, values);
                        refP.ParentNode.InsertAfter(clone, refP);
                    }
                }
                EvaluatePs(TID, bIndex, staticIndex);
            }
            else
            {
                refP.ParentNode.RemoveChild(refP);
            }
        }

        private void EvaluatePs(string TID, bool bIndex, int staticIndex)
        {
            XmlNodeList nodesTobeChanged = SelectByTIDs(TID);
            int j = 0;
            foreach (XmlNode node in nodesTobeChanged)
            {
                if (bIndex)
                {
                    if (j == 0)
                    {
                        j++;
                    }
                    else
                    {
                        ModifyTIDofT(node, j.ToString());
                        j++;
                    }
                }
                else
                {
                    if (j == 0)
                    {
                        j++;
                    }
                    else
                    {
                        ModifyTIDofT(node, staticIndex.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// Given a TID, a CDATA text value this method gets the T-Nodes via TID, 
        /// copies them with their whole paragraphs and sets the Text from the value list.
        /// It is possible to give indexes to the newly produced T-Nodes for addresing them later on. This index can be 
        /// static or a up-counting integer. If the parameter overwrite set true then the copied T-Node is overwritten by the first T-Nodes textvalue
        /// </summary>
        /// <param name="TID">TID</param>
        /// <param name="valuesList">list of the text values</param>
        /// <param name="overWrite">if set to <c>true</c> T-Node containing the placeholder gets overwritten</param>
        /// <param name="bIndex">if set to <c>true</c> an index is set to TIDs of the new T-Nodes for addresing purposes</param>
        /// <param name="staticIndex">integer of a static Value</param>
        public void CloneAndEvaluateCDATA(string TID, string CDATAText, bool overWrite, int staticIndex)
        {
            List<string> CDATAPartValues = new List<string>();
            List<XmlNode> resultNodes = new List<XmlNode>();
            if (string.IsNullOrEmpty(CDATAText))
            {
                CDATAPartValues.Add(string.Empty);
            }
            else
            {
                XmlDocument textModuleXML = new XmlDocument();
                XmlNode docNode = textModuleXML.CreateXmlDeclaration("1.0", "ISO-8859-1", null);
                textModuleXML.AppendChild(docNode);

                XmlNode paragrapsNode = textModuleXML.CreateElement("paragraphs");

                paragrapsNode.InnerXml = CDATAText;
                textModuleXML.AppendChild(paragrapsNode);

                XmlNodeList nodes = textModuleXML.SelectNodes("//p");
                if (nodes.Count == 0)
                {
                    CDATAPartValues.Add(CDATAText);
                }
                else
                {
                    foreach (XmlNode node in nodes)
                    {
                        CDATAPartValues.Add(node.InnerText);
                    }
                }
                CloneAndEvaluatePs(TID, CDATAPartValues.ToArray(), false, false, staticIndex);
            }
        }

        /// <summary>
        /// Moves one or more P nodes from source a position to destination position
        /// given trough a destination P nodes list.
        /// </summary>
        /// <param name="sources">source Ps to be moved</param>
        /// <param name="dest">destination Ps to be moved after</param>
        public void MoveParagraph(XmlNodeList sources, XmlNodeList dest)
        {
            XmlNode destT = dest[dest.Count - 1];
            XmlNode destP = PofT(destT);
            for (int i = sources.Count - 1; i >= 0; i--)
            {
                XmlNode sourceP = PofT(sources[i]);
                XmlNode clone = sourceP.Clone();
                sourceP.ParentNode.RemoveChild(sourceP);
                destP.ParentNode.InsertAfter(clone, destP);
            }

        }

        /// <summary>
        /// given a t node this method returns the P node
        /// </summary>
        /// <param name="tNode">tnode.</param>
        /// <returns></returns>
        public static XmlNode PofT(XmlNode tNode)
        {
            if (tNode.ParentNode.ParentNode != null)
            {
                return tNode.ParentNode.ParentNode;
            }
            return tNode;
        }

        /// <summary>
        /// Removes a P-node via its TID
        /// </summary>
        /// <param name="TID">TID</param>
        public void RemovePbyTID(string TID)
        {
            XmlNode node = SelectByTID(TID);
            if (node != null)
            {
                PofT(node).ParentNode.RemoveChild(PofT(node));
                return;
            }
            else
            {
                return;
            }
        }

        /// <summary>
        /// Modifies the TID of a T node.
        /// </summary>
        /// <param name="T">T node</param>
        /// <param name="appendedStr">string to be appended to the TID value</param>
        private void ModifyTIDofT(XmlNode T, string appendedStr)
        {
            if (T == null)
            {
                return;
            }
            else
            {
                XmlAttribute attr = T.Attributes[0];
                if (attr != null)
                {
                    T.Attributes[0].Value = string.Format("{0}{1}", T.Attributes[0].Value.ToString(), appendedStr);
                }
            }
        }

    }
}
